package com.brillilab.common.alioss;

import com.aliyun.oss.ClientConfiguration;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.common.utils.BinaryUtil;
import com.aliyun.oss.model.*;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.aliyuncs.profile.IClientProfile;
import com.aliyuncs.sts.model.v20150401.AssumeRoleRequest;
import com.aliyuncs.sts.model.v20150401.AssumeRoleResponse;
import com.brillilab.common.constant.ResultEnum;
import com.brillilab.common.exceptions.BrillilabException;
import com.brillilab.common.utils.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.io.*;
import java.net.URL;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.spec.X509EncodedKeySpec;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * @author zlb
 * @Title: OssUtils
 * @ProjectName brill_lab
 * @Description: TODO
 * @date 2019/5/9 14:09
 */
@Component
public class OssUtils {

    /**
     * 日志
     */
    private static Logger logger = LoggerFactory.getLogger(OssUtils.class);

    @Resource
    private AliOssConfig aliOssConfig;

    private static OSSClient ossClient = null;

    /**
     * 初始化ossClient客户端
     *
     * @return
     */
    public OSSClient initClient() {
        if (ossClient == null) {
            synchronized (OssUtils.class) {
                if (ossClient == null) {
                    ClientConfiguration config = new ClientConfiguration();
                    config.setSocketTimeout(aliOssConfig.getSocketTimeout());
                    config.setConnectionTimeout(aliOssConfig.getConnectionTimeout());
                    config.setMaxConnections(aliOssConfig.getMaxConnections());
                    //使用你的对应的endpoint地址
                    String endpoint = aliOssConfig.getEndPoint();

                    // 阿里云主账号AccessKey拥有所有API的访问权限，风险很高。强烈建议您创建并使用RAM账号进行API访问或日常运维，请登录https://ram.console.aliyun.com 创建RAM账号。
                    String accessKeyId = aliOssConfig.getAccessKeyId();
                    String accessKeySecret = aliOssConfig.getAccessKeySecret();
                    ossClient = new OSSClient(endpoint, accessKeyId, accessKeySecret, config);
                }
            }
        }
        return ossClient;
    }

    /**
     * 通过网络流上传文件
     *
     * @param ossClient  oss客户端
     * @param url
     * @param bucketName bucket名称
     * @param objectName 上传文件目录和（包括文件名）例如“test/index.html”
     * @return
     */
    public static PutObjectResult uploadByNetworkStream(OSSClient ossClient, URL url, String bucketName, String objectName) {
        PutObjectResult result = null;
        try {
            InputStream inputStream = url.openStream();
            result = ossClient.putObject(bucketName, objectName, inputStream);
            ossClient.shutdown();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 通过输入流上传文件
     *
     * @param ossClient   oss客户端
     * @param inputStream 输入流
     * @param bucketName  bucket名称
     * @param objectName  上传文件目录和（包括文件名） 例如“test/a.jpg”
     * @return
     */
    public static PutObjectResult uploadByInputStream(OSSClient ossClient, InputStream inputStream, String bucketName,
                                                      String objectName) {
        PutObjectResult result = null;
        try {
            result = ossClient.putObject(bucketName, objectName, inputStream);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 通过file上传文件
     *
     * @param ossClient  oss客户端
     * @param file       上传的文件
     * @param bucketName bucket名称
     * @param objectName 上传文件目录和（包括文件名） 例如“test/a.jpg”
     * @return
     */
    public static PutObjectResult uploadByFile(OSSClient ossClient, File file, String bucketName, String objectName) {
        PutObjectResult result = null;
        try {
            result = ossClient.putObject(bucketName, objectName, file);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 上传本地文件(断点续传)
     *
     * @param ossClient
     * @param filePath
     * @param bucketName
     * @param objectName
     * @return
     * @throws IOException
     */
    public static UploadFileResult uploadLocalFile(OSSClient ossClient, String filePath, String bucketName, String objectName) throws IOException {
        UploadFileResult uploadResult = null;
        try {

            UploadFileRequest uploadFileRequest = new UploadFileRequest(bucketName, objectName);
            // 待上传的本地文件
            uploadFileRequest.setUploadFile(filePath);
            // 设置并发下载数，默认1
            uploadFileRequest.setTaskNum(5);
            // 设置分片大小，默认100KB
            uploadFileRequest.setPartSize(1024 * 1024 * 1);
            // 开启断点续传，默认关闭
            uploadFileRequest.setEnableCheckpoint(true);

            uploadResult = ossClient.uploadFile(uploadFileRequest);
            if (uploadResult != null) {
                CompleteMultipartUploadResult multipartUploadResult =
                        uploadResult.getMultipartUploadResult();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } catch (Throwable e) {
            e.printStackTrace();
        }
//        finally {
//            if (ossClient != null) {
//                ossClient.shutdown();
//            }
//        }
        return uploadResult;
    }

    /**
     * 根据key删除oss服务器上的文件
     *
     * @param ossClient  oss客户端
     * @param bucketName bucket名称
     * @param key        文件路径/名称，例如“test/a.txt”
     */
    public static void deleteFile(OSSClient ossClient, String bucketName, String key) {
        ossClient.deleteObject(bucketName, key);
    }

    /**
     * 根据key获取服务器上的文件的输入流
     *
     * @param ossClient  oss客户端
     * @param bucketName bucket名称
     * @param key        文件路径和名称
     * @return InputStream 文件输入流
     */
    public static InputStream getInputStreamByOSS(OSSClient ossClient, String bucketName, String key) {
        InputStream content = null;
        try {
            OSSObject ossObj = ossClient.getObject(bucketName, key);
            content = ossObj.getObjectContent();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return content;
    }

    /**
     * @param ossClient  oss客户端
     * @param bucketName bucket名称
     * @return List<String>  文件路径和大小集合
     * @throws
     * @title: queryAllObject
     * @Description: 查询某个bucket里面的所有文件
     */
    public static List<String> queryAllObject(OSSClient ossClient, String bucketName) {
        List<String> results = new ArrayList<String>();
        try {
            // ossClient.listObjects返回ObjectListing实例，包含此次listObject请求的返回结果。
            ObjectListing objectListing = ossClient.listObjects(bucketName);
            // objectListing.getObjectSummaries获取所有文件的描述信息。
            for (OSSObjectSummary objectSummary : objectListing.getObjectSummaries()) {
                results.add(" - " + objectSummary.getKey() + "  " + "(size = " + objectSummary.getSize() + ")");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
//        finally {
//            if (ossClient != null) {
//                ossClient.shutdown();
//            }
//        }
        return results;
    }

    /**
     * 创建存储空间
     *
     * @param ossClient  OSS连接
     * @param bucketName 存储空间
     * @return
     */
    public static String createBucketName(OSSClient ossClient, String bucketName) {
        // 存储空间
        final String bucketNames = bucketName;
        if (!ossClient.doesBucketExist(bucketName)) {
            // 创建存储空间
            Bucket bucket = ossClient.createBucket(bucketName);
            //logger.info("创建存储空间成功");
            return bucket.getName();
        }
        return bucketNames;
    }

    /**
     * 删除存储空间buckName
     *
     * @param ossClient  oss对象
     * @param bucketName 存储空间
     */
    public static void deleteBucket(OSSClient ossClient, String bucketName) {
        ossClient.deleteBucket(bucketName);
        logger.info("删除" + bucketName + "Bucket成功");
    }

    /**
     * 创建模拟文件夹
     *
     * @param ossClient  oss连接
     * @param bucketName 存储空间
     * @param folder     模拟文件夹名如"qj_nanjing/"
     * @return 文件夹名
     */
    public static String createFolder(OSSClient ossClient, String bucketName, String folder) {
        // 文件夹名
        final String keySuffixWithSlash = folder;
        // 判断文件夹是否存在，不存在则创建
        if (!ossClient.doesObjectExist(bucketName, keySuffixWithSlash)) {
            // 创建文件夹
            ossClient.putObject(bucketName, keySuffixWithSlash, new ByteArrayInputStream(new byte[0]));
            logger.info("创建文件夹成功");
            // 得到文件夹名
            OSSObject object = ossClient.getObject(bucketName, keySuffixWithSlash);
            String fileDir = object.getKey();
            return fileDir;
        }
        return keySuffixWithSlash;
    }

    /**
     * 获取文件
     *
     * @param ossClient
     * @param bucketName
     * @param key
     * @return
     */
    public static String getObject(OSSClient ossClient, String bucketName, String key) {
        try {
            OSSObject object = ossClient.getObject(bucketName, key);
            InputStream objectContent = object.getObjectContent();
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            int i = -1;
            while ((i = objectContent.read()) != -1) {
                baos.write(i);
            }
            objectContent.close();
            return baos.toString("UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 获取文件访问的外链
     *
     * @param ossClient
     * @param bucketName 存储空间名称
     * @param key        文件key值
     * @return
     */
    public static String getAccessUrl(OSSClient ossClient, String bucketName, String key) {

        // 设置URL过期时间为1小时
        Date expiration = new Date(new Date().getTime() + 3600 * 1000);
        GeneratePresignedUrlRequest generatePresignedUrlRequest;
        generatePresignedUrlRequest = new GeneratePresignedUrlRequest(bucketName, key);
        generatePresignedUrlRequest.setExpiration(expiration);
        URL url = ossClient.generatePresignedUrl(generatePresignedUrlRequest);
        return url.toString();
    }

    /**
     * @throws BrillilabException
     * @Title: getThirdAppUploadPermission
     * @Description: 第三方获取alioss上传权限
     * @return: AliSts
     */
    public AliSts getThirdAppUploadPermission() throws BrillilabException {
        String stsEndpoint = "sts.aliyuncs.com";
        String policy = "{\n" +
                "    \"Version\": \"1\", \n" +
                "    \"Statement\": [\n" +
                "        {\n" +
                "            \"Action\": [\n" +
                "                \"oss:*\"\n" +
                "            ], \n" +
                "            \"Resource\": [\n" +
                "                \"acs:oss:*:*:*\" \n" +
                "            ], \n" +
                "            \"Effect\": \"Allow\"\n" +
                "        }\n" +
                "    ]\n" +
                "}";
        try {
            // 添加endpoint（直接使用STS endpoint，前两个参数留空，无需添加region ID）
            DefaultProfile.addEndpoint("", "", "Sts", stsEndpoint);
            // 构造default profile（参数留空，无需添加region ID）
            IClientProfile profile = DefaultProfile
                    .getProfile("", aliOssConfig.getAccessKeyId(), aliOssConfig.getAccessKeySecret());
            // 用profile构造client
            DefaultAcsClient client = new DefaultAcsClient(profile);
            final AssumeRoleRequest request = new AssumeRoleRequest();
            request.setMethod(MethodType.POST);
            request.setRoleArn("acs:ram::1526382905149969:role/labinoneapp");
            request.setRoleSessionName("appoperator");
            request.setPolicy(policy);
            //request.setPolicy(policy); // Optional
            final AssumeRoleResponse response = client.getAcsResponse(request);
            AliSts sts = new AliSts();
            sts.setAccessKeyId(response.getCredentials().getAccessKeyId());
            sts.setAccessKeySecret(response.getCredentials().getAccessKeySecret());
            sts.setSecurityToken(response.getCredentials().getSecurityToken());
            sts.setExpireTime(response.getCredentials().getExpiration());
            return sts;
        } catch (ClientException e) {
            //logger.error("getThirdAppUploadPermission-Err:"+e.getMessage());
            throw new BrillilabException(ResultEnum.FAILURE);
        }
    }

    /**
     * @param fileUrl 文件url
     * @return String bucketName
     * @MethodName: getBucketName
     * @Description: 根据url获取bucketName
     */
    private static String getBucketNameByUrl(String fileUrl) {
        String http = "http://";
        String https = "https://";
        int httpIndex = fileUrl.indexOf(http);
        int httpsIndex = fileUrl.indexOf(https);
        int startIndex = 0;
        if (httpIndex == -1) {
            if (httpsIndex == -1) {
                return null;
            } else {
                startIndex = httpsIndex + https.length();
            }
        } else {
            startIndex = httpIndex + http.length();
        }
        int endIndex = fileUrl.indexOf(".oss-");
        return fileUrl.substring(startIndex, endIndex);
    }

    /**
     * @param fileUrl 文件url
     * @return String fileName
     * @MethodName: getFileName
     * @Description: 根据url获取fileName
     */
    private static String getFileName(String fileUrl) {
        String str = "aliyuncs.com/";
        int beginIndex = fileUrl.indexOf(str);
        if (beginIndex == -1) return null;
        return fileUrl.substring(beginIndex + str.length());
    }

    /**
     * @param fileUrls 文件url
     * @return List<String>  fileName集合
     * @MethodName: getFileName
     * @Description: 根据url获取fileNames集合
     */
    private static List<String> getFileName(List<String> fileUrls) {
        List<String> names = new ArrayList<>();
        for (String url : fileUrls) {
            names.add(getFileName(url));
        }
        return names;
    }

    /**
     * 获取附件上传基础目录
     *
     * @param attachType  附件类型
     * @param referDataId 业务
     * @return
     */
    public String getFileBasePath(Integer attachType, Long referDataId) {
        Assert.notNull(OssFileEnum.getValue(attachType), "附件类型有误，请核实！");
        String basePath = OssFileEnum.getFileSaveType(attachType) + referDataId + "/" + OssFileEnum.getValue(attachType) + "/"+ DateUtil.dateString(new Date(),"yyMMddHHmmss") +"/";
        return basePath;
    }

    /**
     * 根据附件类型获取存储空间名BucketName
     *
     * @param attachType
     * @return
     */
    public String getBucketName(Integer attachType) {
        String bucketName = aliOssConfig.getPrivateBucket();
        if (OssFileEnum.PersonHeadImg.getKey().equals(attachType) || OssFileEnum.LabLogo.getKey().equals(attachType)) {
            //附件类型为个人头像的存储在公有空间
            bucketName = aliOssConfig.getPublicBucket();
        } else if (OssFileEnum.VideoPreviewImg.getKey().equals(attachType)) {
            //附件类型为视频预览图的存储在公有空间
            bucketName = aliOssConfig.getPublicBucket();
        } else if (OssFileEnum.PublicMethod.getKey().equals(attachType)) {
            //公有方法-公共
            bucketName = aliOssConfig.getPublicBucket();
        } else if (OssFileEnum.PublicFile.getKey().equals(attachType) || OssFileEnum.CrmPayVoucher.getKey().equals(attachType)) {
            //公共文件
            bucketName = aliOssConfig.getPublicBucket();
        } else if (attachType >= OssFileEnum.AnalyzeDataFile.getKey() && attachType <= OssFileEnum.AnalyzeDataFileThumbnail.getKey()) {
            //实验结果数据单独存储在独立的存储空间，（分析后数据、分析后缩略图除外）
            if(!attachType.equals(OssFileEnum.AnalyzeDataFile.getKey()) && !attachType.equals(OssFileEnum.AnalyzeDataFileThumbnail.getKey())) {
                bucketName = aliOssConfig.getExperimentResultBucket();
            }
        } else if (attachType >= 500 && attachType < 600) {
            //课题数据单独存储在独立的存储空间
            bucketName = aliOssConfig.getTopicBucket();
        }
        return bucketName;
    }

    /**
     * 根据附件类型获取host
     *
     * @param attachType
     * @return
     */
    public String getHostUrl(Integer attachType) {
        String hostUrl;
        if (attachType >= OssFileEnum.AnalyzeDataFile.getKey() && attachType <= OssFileEnum.AnalyzeDataFileThumbnail.getKey()) {
            //实验结果数据单独存储在独立的存储空间
            hostUrl = "http://" + aliOssConfig.getExperimentResultBucket() + "." +
                    aliOssConfig.getEndPoint().replace("http://", "") + "/";

            //分析后数据、分析后缩略图放置到私有空间下
            if(attachType.equals(OssFileEnum.AnalyzeDataFile.getKey()) || attachType.equals(OssFileEnum.AnalyzeDataFileThumbnail.getKey())) {
                hostUrl = getPrivateHost();
            }
        } else if (attachType >= 500 && attachType < 600) {
            //课题数据单独存储在独立的存储空间
            hostUrl = "http://" + aliOssConfig.getTopicBucket() + "." +
                    aliOssConfig.getEndPoint().replace("http://", "") + "/";
        }
        else {
            if (OssFileEnum.getIsPublicBucket(attachType)) {
                //公有存储空间
                hostUrl = getPublicHost();
            }
            else {
                //私有存储空间
                hostUrl = getPrivateHost();
            }
        }
        return hostUrl;
    }

    /**
     * 获取公有目录主地址
     *
     * @return
     */
    public String getPublicHost() {
        return "http://" + aliOssConfig.getPublicBucket() + "." +
                aliOssConfig.getEndPoint().replace("http://", "") + "/";
    }

    /**
     * 获取私有目录主地址
     *
     * @return
     */
    public String getPrivateHost() {
        return "http://" + aliOssConfig.getPrivateBucket() + "." +
                aliOssConfig.getEndPoint().replace("http://", "") + "/";
    }

    public String getPostBody(InputStream is, int contentLen) throws IOException {
        if (contentLen > 0) {
            int readLen = 0;
            int readLengthThisTime;
            byte[] message = new byte[contentLen];
            while (readLen != contentLen) {
                readLengthThisTime = is.read(message, readLen, contentLen - readLen);
                if (readLengthThisTime == -1) {// Should not happen.
                    break;
                }
                readLen += readLengthThisTime;
            }
            return new String(message);
        }
        return "";
    }

    public static boolean doCheck(String content, byte[] sign, String publicKey) {
        try {
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            byte[] encodedKey = BinaryUtil.fromBase64String(publicKey);
            PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(encodedKey));
            java.security.Signature signature = java.security.Signature.getInstance("MD5withRSA");
            signature.initVerify(pubKey);
            signature.update(content.getBytes());
            return signature.verify(sign);

        } catch (Exception e) {
            e.printStackTrace();
        }

        return false;
    }


    /**
     * @param fileType
     * @return String
     * @MethodName: contentType
     * @Description: 获取文件类型
     */
    private static String contentType(String fileType) {
        fileType = fileType.toLowerCase();
        String contentType = "";
        if (fileType.equals("bmp")) {
            contentType = "image/bmp";

        } else if (fileType.equals("gif")) {
            contentType = "image/gif";

        } else if (fileType.equals("png") || fileType.equals("jpeg")
                || fileType.equals("jpg")) {
            contentType = "image/jpeg";

        } else if (fileType.equals("html")) {
            contentType = "text/html";

        } else if (fileType.equals("txt")) {
            contentType = "text/plain";

        } else if (fileType.equals("vsd")) {
            contentType = "application/vnd.visio";

        } else if (fileType.equals("ppt") || fileType.equals("pptx")) {
            contentType = "application/vnd.ms-powerpoint";

        } else if (fileType.equals("doc") || fileType.equals("docx")) {
            contentType = "application/msword";

        } else if (fileType.equals("xml")) {
            contentType = "text/xml";

        } else if (fileType.equals("mp4")) {
            contentType = "video/mp4";
        } else {
            contentType = "application/octet-stream";

        }
        return contentType;
    }

    /**
     * 获取文件大小
     *
     * @param size 字节大小
     * @return
     */
    public static String getFileSize(long size) {
        //如果字节数少于1024，则直接以B为单位，否则先除于1024，后3位因太少无意义
        if (size < 1024) {
            return String.format("%sB", String.valueOf(size));
        } else {
            size = size / 1024;
        }
        //如果原字节数除于1024之后，少于1024，则可以直接以KB作为单位
        //因为还没有到达要使用另一个单位的时候
        //接下去以此类推
        if (size < 1024) {
            return String.format("%sKB", String.valueOf(size));
        } else {
            size = size / 1024;
        }
        if (size < 1024) {
            //因为如果以MB为单位的话，要保留最后1位小数，
            //因此，把此数乘以100之后再取余
            size = size * 100;
            return String.format("%s.%sMB", String.valueOf((size / 100)), String.valueOf((size % 100)));
        } else {
            //否则如果要以GB为单位的，先除于1024再作同样的处理
            size = size * 100 / 1024;
            return String.format("%s.%sGB", String.valueOf((size / 100)), String.valueOf((size % 100)));
        }
    }
}
